{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### YAML Format"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "YAML, like JSON, is another data serialization standard. It is actually easier to read than JSON, and although it has been around for a long time (since 2001), it has gained a lot of popularity, especially in the Dev Ops world for configuration files (Docker, Kubernetes, etc).\n",
    "\n",
    "Like JSON it is able to represent simple data types (strings, numbers, boolean, etc) as well as collections and associative arrays (dictionaries).\n",
    "\n",
    "YAML focuses on human readability, and is a little more complex to parse.\n",
    "\n",
    "Here is a sample YAML file:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "title: Parrot Sketch\n",
    "year: 1989\n",
    "actors:\n",
    "    - first_name: John\n",
    "      last_name: Cleese\n",
    "      dob: 1939-10-27\n",
    "    - first_name: Michael\n",
    "      last_name: Palin\n",
    "      dob: 1943-05-05\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see this is much easier to read than JSON or XML."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To parse YAML into a Python dictionary would take a fair amount of work - especially since YAML is quite flexible."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fortunately, we can use the 3rd party library, `pyyaml` to do this for us.\n",
    "\n",
    "Again, I'm only going to show you a tiny bit of this library, and you can read more about it here:\n",
    "https://pyyaml.org/wiki/PyYAMLDocumentation\n",
    "\n",
    "(It's definitely less of a learning curve than Marshmallow!!)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Caution\n",
    "When you load a yaml file using pyyaml, be careful - like pickling it can actually call out to Python functions - so do not load untrusted YAML files using `pyyaml`!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import yaml"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = '''\n",
    "---\n",
    "title: Parrot Sketch\n",
    "year: 1989\n",
    "actors:\n",
    "    - first_name: John\n",
    "      last_name: Cleese\n",
    "      dob: 1939-10-27\n",
    "    - first_name: Michael\n",
    "      last_name: Palin\n",
    "      dob: 1943-05-05\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = yaml.load(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "dict"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "type(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'actors': [{'dob': datetime.date(1939, 10, 27),\n",
      "             'first_name': 'John',\n",
      "             'last_name': 'Cleese'},\n",
      "            {'dob': datetime.date(1943, 5, 5),\n",
      "             'first_name': 'Michael',\n",
      "             'last_name': 'Palin'}],\n",
      " 'title': 'Parrot Sketch',\n",
      " 'year': 1989}\n"
     ]
    }
   ],
   "source": [
    "from pprint import pprint\n",
    "\n",
    "pprint(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll notice that unlike the built-in JSON parser, PyYAML was able to automatically deduce the `date` type in our YAML, as well of course as strings and integers.\n",
    "\n",
    "Of course, serialization works the same way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = {'a': 100, 'b': False, 'c': 10.5, 'd': [1, 2, 3]}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: 100\n",
      "b: false\n",
      "c: 10.5\n",
      "d: [1, 2, 3]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(yaml.dump(d))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You'll notice in the above example that the list was represented using `[1, 2, 3]` - this is valid YAML as well, and is equivalent to this notation:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "d:\n",
    "    - 1\n",
    "    - 2\n",
    "    - 3\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you prefer this block style, you can force it this way:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: 100\n",
      "b: false\n",
      "c: 10.5\n",
      "d:\n",
      "- 1\n",
      "- 2\n",
      "- 3\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(yaml.dump(d, default_flow_style=False))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What's interesting about PyYAML is that it can also automatically serialize and deserialize complex objects:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person:\n",
    "    def __init__(self, name, dob):\n",
    "        self.name = name\n",
    "        self.dob = dob\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Person(name={self.name}, dob={self.dob})'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "from datetime import date\n",
    "\n",
    "p1 = Person('John Cleese', date(1939, 10, 27))\n",
    "p2 = Person('Michael Palin', date(1934, 5, 5))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "john: !!python/object:__main__.Person {dob: 1939-10-27, name: John Cleese}\n",
      "michael: !!python/object:__main__.Person {dob: 1934-05-05, name: Michael Palin}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(yaml.dump({'john': p1, 'michael': p2}))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that weird looking syntax? It's actually useful when we deserialize the YAML string - of course it means we must have a `Person` class defined with the appropriate init method."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "yaml_data = '''\n",
    "john: !!python/object:__main__.Person \n",
    "    dob: 1939-10-27\n",
    "    name: John Cleese\n",
    "michael: !!python/object:__main__.Person \n",
    "    dob: 1934-05-05\n",
    "    name: Michael Palin\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "d = yaml.load(yaml_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'john': Person(name=John Cleese, dob=1939-10-27),\n",
       " 'michael': Person(name=Michael Palin, dob=1934-05-05)}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, `john` and `michael` were deserialized into `Person` type objects.\n",
    "\n",
    "This is why you have to be quite careful with the source of any YAML you deserialize.\n",
    "\n",
    "Here's an evil example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "yaml_data = '''\n",
    "exec_paths: \n",
    "    !!python/object/apply:os.get_exec_path []\n",
    "exec_command:\n",
    "    !!python/object/apply:subprocess.check_output [['ls', '/']]\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'exec_paths': ['/Users/fbaptiste/anaconda3/envs/deepdive/bin',\n",
       "  '/Users/fbaptiste/anaconda3/envs/deepdive/bin',\n",
       "  '/Users/fbaptiste/anaconda3/bin',\n",
       "  '/usr/local/bin',\n",
       "  '/usr/bin',\n",
       "  '/bin',\n",
       "  '/usr/sbin',\n",
       "  '/sbin'],\n",
       " 'exec_command': b'Applications\\nLibrary\\nNetwork\\nSystem\\nUsers\\nVolumes\\nbin\\ncores\\ndev\\netc\\nhome\\ninstaller.failurerequests\\nnet\\nprivate\\nsbin\\ntmp\\nusr\\nvar\\n'}"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "yaml.load(yaml_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, be very careful with `load`. In general it is safer practice to use the `safe_load` method instead, but you will lose the ability to deserialize into custom Python objects, unless you override that behavior. You can always use Marshmallow to do that secondary step in a safer way."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "ename": "ConstructorError",
     "evalue": "could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/apply:os.get_exec_path'\n  in \"<unicode string>\", line 3, column 5:\n        !!python/object/apply:os.get_exe ... \n        ^",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mConstructorError\u001b[0m                          Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-17-a4c68930d90a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0myaml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msafe_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myaml_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/__init__.py\u001b[0m in \u001b[0;36msafe_load\u001b[0;34m(stream)\u001b[0m\n\u001b[1;32m     92\u001b[0m     \u001b[0mResolve\u001b[0m \u001b[0monly\u001b[0m \u001b[0mbasic\u001b[0m \u001b[0mYAML\u001b[0m \u001b[0mtags\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     93\u001b[0m     \"\"\"\n\u001b[0;32m---> 94\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mSafeLoader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     95\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     96\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msafe_load_all\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/__init__.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(stream, Loader)\u001b[0m\n\u001b[1;32m     70\u001b[0m     \u001b[0mloader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     71\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 72\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_single_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     73\u001b[0m     \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m         \u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mget_single_data\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     35\u001b[0m         \u001b[0mnode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_single_node\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     36\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mnode\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 37\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_document\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     38\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     39\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_document\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m     44\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate_generators\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     45\u001b[0m             \u001b[0;32mfor\u001b[0m \u001b[0mgenerator\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mstate_generators\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m                 \u001b[0;32mfor\u001b[0m \u001b[0mdummy\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgenerator\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     47\u001b[0m                     \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     48\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstructed_objects\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_yaml_map\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m    396\u001b[0m         \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    397\u001b[0m         \u001b[0;32myield\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 398\u001b[0;31m         \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    399\u001b[0m         \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    400\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_mapping\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m    202\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMappingNode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    203\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflatten_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 204\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdeep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdeep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    205\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    206\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mconstruct_yaml_null\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_mapping\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m    127\u001b[0m                 raise ConstructorError(\"while constructing a mapping\", node.start_mark,\n\u001b[1;32m    128\u001b[0m                         \"found unhashable key\", key_node.start_mark)\n\u001b[0;32m--> 129\u001b[0;31m             \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_object\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue_node\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdeep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdeep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    130\u001b[0m             \u001b[0mmapping\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    131\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_object\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m     84\u001b[0m                     \u001b[0mconstructor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     85\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mtag_suffix\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 86\u001b[0;31m             \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconstructor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     87\u001b[0m         \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     88\u001b[0m             \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconstructor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtag_suffix\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_undefined\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m    412\u001b[0m         raise ConstructorError(None, None,\n\u001b[1;32m    413\u001b[0m                 \u001b[0;34m\"could not determine a constructor for the tag %r\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtag\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 414\u001b[0;31m                 node.start_mark)\n\u001b[0m\u001b[1;32m    415\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    416\u001b[0m SafeConstructor.add_constructor(\n",
      "\u001b[0;31mConstructorError\u001b[0m: could not determine a constructor for the tag 'tag:yaml.org,2002:python/object/apply:os.get_exec_path'\n  in \"<unicode string>\", line 3, column 5:\n        !!python/object/apply:os.get_exe ... \n        ^"
     ]
    }
   ],
   "source": [
    "yaml.safe_load(yaml_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To override and allow certain Python objects to be deserialized in `safe_load` we can proceed this way.\n",
    "\n",
    "Firstly we are going to simplify the object tag notation by customizing it in our `Person` class, and we are also going to make our object as safe to be deserialized. Our `Person` class will now have to inherit from the `yaml.YAMLObject`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "from yaml import YAMLObject, SafeLoader\n",
    "\n",
    "class Person(YAMLObject):\n",
    "    yaml_tag = '!Person'\n",
    "    \n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Person(name={self.name}, age={self.age})'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First let's see how objects are now serialized:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'john: !Person {age: 79, name: John Cleese}\\nmichael: !Person {age: 74, name: Michael Palin}\\n'"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "yaml.dump(dict(john=Person('John Cleese', 79),\n",
    "               michael=Person('Michael Palin', 74)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see we have a slightly cleaner syntax.\n",
    "\n",
    "Now let's try to load the serialized version:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "yaml_data = '''\n",
    "john: !Person\n",
    "    name: John Cleese\n",
    "    age: 79\n",
    "michael: !Person\n",
    "    name: Michael Palin\n",
    "    age: 74\n",
    "'''"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'john': Person(name=John Cleese, age=79),\n",
       " 'michael': Person(name=Michael Palin, age=74)}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "yaml.load(yaml_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And `safe_load`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "ename": "ConstructorError",
     "evalue": "could not determine a constructor for the tag '!Person'\n  in \"<unicode string>\", line 2, column 7:\n    john: !Person\n          ^",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mConstructorError\u001b[0m                          Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-22-a4c68930d90a>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0myaml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msafe_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myaml_data\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/__init__.py\u001b[0m in \u001b[0;36msafe_load\u001b[0;34m(stream)\u001b[0m\n\u001b[1;32m     92\u001b[0m     \u001b[0mResolve\u001b[0m \u001b[0monly\u001b[0m \u001b[0mbasic\u001b[0m \u001b[0mYAML\u001b[0m \u001b[0mtags\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     93\u001b[0m     \"\"\"\n\u001b[0;32m---> 94\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mSafeLoader\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     95\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     96\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0msafe_load_all\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/__init__.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(stream, Loader)\u001b[0m\n\u001b[1;32m     70\u001b[0m     \u001b[0mloader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mLoader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstream\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     71\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 72\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_single_data\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     73\u001b[0m     \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     74\u001b[0m         \u001b[0mloader\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdispose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mget_single_data\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m     35\u001b[0m         \u001b[0mnode\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mget_single_node\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     36\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mnode\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 37\u001b[0;31m             \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_document\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     38\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     39\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_document\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m     44\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstate_generators\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     45\u001b[0m             \u001b[0;32mfor\u001b[0m \u001b[0mgenerator\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mstate_generators\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 46\u001b[0;31m                 \u001b[0;32mfor\u001b[0m \u001b[0mdummy\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mgenerator\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     47\u001b[0m                     \u001b[0;32mpass\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     48\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstructed_objects\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_yaml_map\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m    396\u001b[0m         \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    397\u001b[0m         \u001b[0;32myield\u001b[0m \u001b[0mdata\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 398\u001b[0;31m         \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    399\u001b[0m         \u001b[0mdata\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    400\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_mapping\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m    202\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mMappingNode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    203\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflatten_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 204\u001b[0;31m         \u001b[0;32mreturn\u001b[0m \u001b[0msuper\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnode\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdeep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdeep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    205\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    206\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mconstruct_yaml_null\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_mapping\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m    127\u001b[0m                 raise ConstructorError(\"while constructing a mapping\", node.start_mark,\n\u001b[1;32m    128\u001b[0m                         \"found unhashable key\", key_node.start_mark)\n\u001b[0;32m--> 129\u001b[0;31m             \u001b[0mvalue\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_object\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mvalue_node\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdeep\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mdeep\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    130\u001b[0m             \u001b[0mmapping\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mkey\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mvalue\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    131\u001b[0m         \u001b[0;32mreturn\u001b[0m \u001b[0mmapping\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_object\u001b[0;34m(self, node, deep)\u001b[0m\n\u001b[1;32m     84\u001b[0m                     \u001b[0mconstructor\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__class__\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mconstruct_mapping\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     85\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mtag_suffix\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 86\u001b[0;31m             \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconstructor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     87\u001b[0m         \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     88\u001b[0m             \u001b[0mdata\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mconstructor\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtag_suffix\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/anaconda3/envs/deepdive/lib/python3.6/site-packages/yaml/constructor.py\u001b[0m in \u001b[0;36mconstruct_undefined\u001b[0;34m(self, node)\u001b[0m\n\u001b[1;32m    412\u001b[0m         raise ConstructorError(None, None,\n\u001b[1;32m    413\u001b[0m                 \u001b[0;34m\"could not determine a constructor for the tag %r\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mnode\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtag\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 414\u001b[0;31m                 node.start_mark)\n\u001b[0m\u001b[1;32m    415\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    416\u001b[0m SafeConstructor.add_constructor(\n",
      "\u001b[0;31mConstructorError\u001b[0m: could not determine a constructor for the tag '!Person'\n  in \"<unicode string>\", line 2, column 7:\n    john: !Person\n          ^"
     ]
    }
   ],
   "source": [
    "yaml.safe_load(yaml_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So now let's mark our `Person` object as safe:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person(YAMLObject):\n",
    "    yaml_tag = '!Person'\n",
    "    yaml_loader = SafeLoader\n",
    "    \n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        \n",
    "    def __repr__(self):\n",
    "        return f'Person(name={self.name}, age={self.age})'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'john': Person(name=John Cleese, age=79),\n",
       " 'michael': Person(name=Michael Palin, age=74)}"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "yaml.safe_load(yaml_data)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And as you can see, the deserializtion now works for the `Person` class."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There's a lot more this library can do, so look at the reference if you want to use YAML. \n",
    "\n",
    "Also, as I mentionmed before, you can combine this with `Marshmallow` for example to get to a full marshalling solution to complex (custom) Python types."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
